package bss

import (
    "encoding/json"
    "errors"
    "fcs-api/src/app/model"
    "fcs-api/src/app/utils"
    "fcs-api/src/client/kubernetes"
    "fmt"
    "time"

    "git.inspur.com/x/auditlog"
    apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

    commonError "git.inspur.com/x/common/error"
    "github.com/google/uuid"
    "github.com/streadway/amqp"
)

const dateLayout = "2006-01-02 15:04:05"
const contentType = "application/json"
const addUser = "add"
const removeUser = "remove"

func receiveOrder(msg amqp.Delivery) {
    logger.Info("Receive oss order message: %s", string(msg.Body))
    orderReceive := new(OrderReceive)
    err := json.Unmarshal(msg.Body, orderReceive)
    if err != nil {
        logger.Info("ReceiveOrder: Unmarshal msg.Body err: %s", err.Error())
    }
    auditlog.OrderFilter(msg.Body, nil)
    if err == nil {
        if len(orderReceive.ProductList) == 0 || orderReceive.ProductList[0].InstanceId == "" {
            err = errors.New("product list len is 0 or instanceid is nill")
        }
    }
    if err == nil {
        order := &model.Order{
            Id:         uuid.New().String(),
            AccountId:  orderReceive.UserId,
            OrderId:    orderReceive.OrderId,
            ConsoleId:  orderReceive.ConsoleOrderFlowId,
            InstanceId: orderReceive.ProductList[0].InstanceId,
            Status:     "normal",
        }
        _, err = model.CreateOrder(order)
    }
    now := time.Now()
    orderSend := OrderSend{
        UserId:             orderReceive.UserId,
        ConsoleOrderFlowId: orderReceive.ConsoleOrderFlowId,
        OrderId:            orderReceive.OrderId,
        ProductSetList: []*ProductSet{{
            ProductSetStatus: "success",
            Code:             "200",
            Msg:              "success",
            ProductList: []*SendProduct{
                {
                    Region:          orderReceive.ProductList[0].Region,
                    AvailableZone:   orderReceive.ProductList[0].AvailableZone,
                    ProductLineCode: orderReceive.ProductList[0].ProductLineCode,
                    ProductTypeCode: orderReceive.ProductList[0].ProductTypeCode,
                    InstanceId:      orderReceive.ProductList[0].InstanceId,
                    InstanceStatus:  "available",
                    StatusTime:      now.Format(dateLayout),
                    ItemList:        orderReceive.ProductList[0].ItemList,
                },
            },
        }},
    }
    if err != nil {
        _ = logger.Error("ReceiveOrder: insert order error, %s", err.Error())
        orderSend.ProductSetList[0].Code = model.ErrorBaseCode + model.OrderHandleError
        orderSend.ProductSetList[0].Msg = err.Error()
        orderSend.ProductSetList[0].ProductList[0].InstanceStatus = "unavailable"
    }
    err = sendOrder(orderSend)
    if err != nil {
        _ = logger.Error("ReceiveOrder: send order error, %s", err.Error())
    }
}

func sendOrder(send OrderSend) error {
    v, err := json.Marshal(send)
    if err != nil {
        return err
    }
    args := make(amqp.Table)
    args["x-dead-letter-exchange"] = model.Base.BssMqInfo.DeadLetterExchange
    args["x-dead-letter-routing-key"] = model.Base.BssMqInfo.DeadLetterRouteKey
    return mq.Send(model.Base.BssMqInfo.Exchange, model.Base.BssMqInfo.OrderCallbackQueue,
        model.Base.BssMqInfo.OrderCallbackRouteKey, contentType, v, args)
}

func receiveStatus(msg amqp.Delivery) {
    logger.Info("receiveStatus: Receive oss status message: %s", string(msg.Body))
    status := new(Status)
    err := json.Unmarshal(msg.Body, status)
    if err != nil {
        logger.Info("receiveStatus: Unmarshal msg.Body err: %s", err.Error())
        return
    }
    switch status.InstanceList[0].OperateType {
    case "stopServer":
        err = model.UpdateOrderStatus(status.InstanceList[0].InstanceId, "stop")
        if err == nil {
            err = updateUserList(status.InstanceList[0].UserId, addUser)
        }
    case "resumeServer":
        err = model.UpdateOrderStatus(status.InstanceList[0].InstanceId, "normal")
        if err == nil {
            err = updateUserList(status.InstanceList[0].UserId, removeUser)
        }
    default:
        err = errors.New(fmt.Sprintf("operateType error, type is %s", status.InstanceList[0].OperateType))
    }
    status.InstanceList[0].StatusTime = time.Now().Format(dateLayout)
    if err != nil {
        status.InstanceList[0].Result = "fail"
        status.InstanceList[0].InstanceStatus = "stopped"
        _ = logger.Error("receiveStatus: change status error, %s", err.Error())
    } else {
        status.InstanceList[0].Result = "success"
        status.InstanceList[0].InstanceStatus = "available"
    }
    err = sendStatus(status)
    if err != nil {
        _ = logger.Error("receiveStatus: send status error, %s", err.Error())
    }
}

func updateUserList(id, action string) error {
    config, err := model.Base.ClusterManager.FCConfig()
    if err != nil {
        _ = logger.Error("updateUserList: get ClusterManager config error %v", err)
        return commonError.InternalServerError(model.FunctionClientError, err.Error())
    }
    adminToken := utils.GetAdminToken()
    config.BearerToken = adminToken
    // 防止同时修改一个文件的时候有冲突，这里重试10次
    for i := 0; i < 10; i++ {
        fcClient, err := kubernetes.NewClientFromConfig(config)
        if err != nil {
            _ = logger.Error("updateUserList: get cmanagerconfig error %v", err)
            continue
        }
        kcp, err := fcClient.GetKongClusterPlugin(model.KCPBss)
        if err != nil {
            _ = logger.Error("updateUserList: get kcp error %v", err)
            continue
        }
        var idList []string

        originConfig, err := json.Marshal(kcp.Config)
        if err != nil {
            _ = logger.Error("updateUserList: kcp.Config.Marshal error", err)
            continue
        }
        var originConfigData model.KcpBssConfig
        err = json.Unmarshal(originConfig, &originConfigData)
        if err != nil {
            _ = logger.Error("updateUserList: json.Unmarshal(originConfig, &originConfigData) error", err)
            continue
        }

        idList = originConfigData.DisabledList

        switch action {
        case addUser:
            idList = append(idList, id)
        case removeUser:
            for i, s := range idList {
                if s == id {
                    idList = append(idList[:i], idList[i+1:]...)
                    break
                }
            }
        }

        originConfigByte, err := json.Marshal(originConfigData)
        if err != nil {
            _ = logger.Error("updateUserList: json.Marshal(originConfigData) error", err)
            continue
        }
        kcp.Config = apiextensionsv1.JSON{Raw: originConfigByte}
        _, err = fcClient.UpdateKongClusterPlugin(kcp)
        if err != nil {
            _ = logger.Error("updateUserList: update kcp error %v", err)
            continue
        }
        return nil
    }
    return errors.New("updateUserList error")
}

func sendStatus(status *Status) error {
    v, err := json.Marshal(status)
    if err != nil {
        return err
    }
    args := make(amqp.Table)
    args["x-dead-letter-exchange"] = model.Base.BssMqInfo.DeadLetterExchange
    args["x-dead-letter-routing-key"] = model.Base.BssMqInfo.DeadLetterRouteKey
    return mq.Send(model.Base.BssMqInfo.Exchange, model.Base.BssMqInfo.ChangeCallbackQueue,
        model.Base.BssMqInfo.ChangeCallbackRouteKey, contentType, v, args)
}

func SendBill(bssOrderList []*BssOrder) {
    billList := make([]*model.Bill, 0)
    for _, order := range bssOrderList {
        v, err := json.Marshal(order)
        if err != nil {
            _ = logger.Error("SendBill: covert json error, %s", err.Error())
            continue
        }
        bill := &model.Bill{
            Id:         uuid.New().String(),
            AccountId:  order.UserId,
            InstanceId: order.ProductList[0].InstanceId,
            BillTime:   order.BillCycle,
            BillDetail: string(v),
            Status:     "success",
        }
        args := make(amqp.Table)
        args["x-dead-letter-exchange"] = model.Base.BssMqInfo.DeadLetterExchange
        args["x-dead-letter-routing-key"] = model.Base.BssMqInfo.DeadLetterRouteKey
        args["x-message-ttl"] = model.Base.BssMqInfo.MessageTTL
        err = mq.Send(model.Base.BssMqInfo.Exchange, model.Base.BssMqInfo.BillQueue, model.Base.BssMqInfo.BillRouteKey, contentType, v, args)
        if err != nil {
            _ = logger.Error("SendBill: send bill error, %s bill info is %s", err.Error(), string(v))
            bill.Status = "fail"
        }
        billList = append(billList, bill)
    }
    _, err := model.CreateBill(billList)
    if err != nil {
        _ = logger.Error("SendBill: insert into bill database error, %s", err.Error())
    }
}
